package com.ttxs.fileformathandling.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ttxs.apicommon.model.entity.User;
import com.ttxs.fileformathandling.common.ErrorCode;
import com.ttxs.fileformathandling.constant.CommonConstant;
import com.ttxs.fileformathandling.exception.BusinessException;
import com.ttxs.fileformathandling.model.dto.file.FileRecordQueryRequest;
import com.ttxs.fileformathandling.model.dto.file.FileRecordRequest;
import com.ttxs.fileformathandling.model.dto.file.UploadFileRequest;
import com.ttxs.fileformathandling.model.dto.file.UploadFileResult;
import com.ttxs.fileformathandling.model.entity.FileRecord;

import com.ttxs.fileformathandling.model.vo.FileRecordVO;
import com.ttxs.fileformathandling.model.vo.UserVO;
import com.ttxs.fileformathandling.service.FileRecordService;
import com.ttxs.fileformathandling.mapper.FileRecordMapper;
import com.ttxs.fileformathandling.service.UserService;
import com.ttxs.fileformathandling.utils.*;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * @author cwt
 * @description 针对表【file_record(用户上传文件表)】的数据库操作Service实现
 * @createDate 2023-05-27 23:05:19
 */
@Service
public class FileRecordServiceImpl extends ServiceImpl<FileRecordMapper, FileRecord>
        implements FileRecordService {

    @Value("${upload.address}")
    private String webAddress;

    @Value("${upload.path}")
    private String basicPath;

    @Resource
    private UserService userService;

    @Override
    public List<FileRecordVO> getRecordVOList(List<FileRecord> recordList) {
        if (recordList == null || recordList.size() == 0) {
            return new ArrayList<>();
        }
        ArrayList<FileRecordVO> fileRecordVOList = new ArrayList<>(recordList.size());
        for (FileRecord fileRecord : recordList) {
            FileRecordVO fileRecordVO = new FileRecordVO();
            BeanUtils.copyProperties(fileRecord, fileRecordVO);
            fileRecordVOList.add(fileRecordVO);
        }
        return fileRecordVOList;
    }

    @Override
    public QueryWrapper<FileRecord> getQueryWrapper(FileRecordQueryRequest fileRecordQueryRequest, HttpServletRequest request) {
        if (fileRecordQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Long userId = fileRecordQueryRequest.getId();
        String userName = fileRecordQueryRequest.getUserName();
        String doType = fileRecordQueryRequest.getDoType();
        Integer recordStatus = fileRecordQueryRequest.getRecordStatus();
        Date createTime = fileRecordQueryRequest.getCreateTime();
        Integer isDelete = fileRecordQueryRequest.getIsDelete();
        String sortField = fileRecordQueryRequest.getSortField();
        String sortOrder = fileRecordQueryRequest.getSortOrder();
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.like(StringUtils.isNotBlank(userName), "userName", userName);
        List<User> list = userService.list(userQueryWrapper);
        ArrayList<Long> idList = new ArrayList<>();
        for (User user : list) {
            Long id = user.getId();
            if (id != null) {
                idList.add(id);
            }
        }
        QueryWrapper<FileRecord> queryWrapper = new QueryWrapper<>();
        //管理员可查看所有用户
        if (userService.notAdmin(request)) {
            queryWrapper.in(idList.size() > 0, "userId", idList);
        }
        queryWrapper.eq(userId != null, "userId", userId);
        //todo  可自定义日期
        queryWrapper.between(createTime != null, "createTime", createTime, new Date());
        queryWrapper.eq(isDelete != null, "isDelete", isDelete);
        queryWrapper.eq(recordStatus != null, "recordStatus", recordStatus);
        queryWrapper.eq(StringUtils.isNotBlank(doType), "doType", doType);
        //默认以创建时间降序排序
        queryWrapper.orderByDesc("createTime");
        queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_DESC), sortField);
        return queryWrapper;
    }

    @Override
    public void validApi(FileRecord fileRecord, boolean add) {
        if (fileRecord == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数不能为空");
        }
        if (StringUtils.isBlank(fileRecord.getInputPath())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "输入路径不能为空");
        }
        if (StringUtils.isBlank(fileRecord.getOutputPath())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "输出路径不能为空");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)   //开启事务
    public UploadFileResult uploadFile(UploadFileRequest uploadFileRequest, HttpServletRequest request) {
        User loginUser = userService.getLoginUser(request);

        //压制普通用户
        if (userService.notAdmin(request)) {
            if (loginUser.getMaxCount() < loginUser.getCurrentCount()) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "操作次数不足");
            }
        }

        Long userId = loginUser.getId();
        if (!userId.equals(uploadFileRequest.getId())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不匹配");
        }

        String inDirStr = basicPath + loginUser.getId() + "/input/";
        String outDirStr = basicPath + loginUser.getId() + "/output/";

        // 构建上传文件的存放 "文件夹" 路径
        File inDir = new File(inDirStr);
        File outDir = new File(outDirStr);
        if (!inDir.exists()) {
            if (!inDir.mkdirs()) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "文件目录创建失败");
            }
        }

        if (!outDir.exists()) {
            if (!outDir.mkdirs()) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "文件目录创建失败");
            }
        }
        //返回结果
        UploadFileResult uploadFileResult = new UploadFileResult();

        MultipartFile file = uploadFileRequest.getFile();
        String content = uploadFileRequest.getContent();
        //用于获取文件类型
        String doType = uploadFileRequest.getDoType();
        String fileName;
        String fileSuffix;
        String newFileName; //新文件名称
        File inFile;
        if (file != null) {
            //校验文件格式是否匹配
            if (!fileTypeRight(file)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "暂不支持此类文件");
            }

            String originalFilename = file.getOriginalFilename();
            assert originalFilename != null;
            int index = originalFilename.lastIndexOf(".");
            fileName = originalFilename.substring(0, index);
            fileSuffix = originalFilename.substring(index + 1).toLowerCase();
            if (fileSuffix.charAt(0) != doType.charAt(0)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "请上传符合操作的文件类型");
            }
            // 只有当满足文件格式时才进来，重新赋文件名，防止出现名称重复的情况
            newFileName = RandomStringUtils.randomAlphanumeric(8) + "-" + fileName + "." + fileSuffix;

            // 该方法返回的为当前项目的工作目录，即在哪个地方启动的java线程
            inFile = new File(inDir, newFileName);
            try {
                file.transferTo(inFile); //转换文件
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else if (StringUtils.isNotBlank(content)) {
            fileName = RandomStringUtils.randomAlphanumeric(5);
            if (doType.startsWith("j")) {
                fileSuffix = "json";
            } else if (doType.startsWith("x")) {
                fileSuffix = "xml";
            } else if (doType.startsWith("y")) {
                fileSuffix = "yaml";
            } else if (doType.startsWith("c")) {
                fileSuffix = "csv";
            } else {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "暂不支持该功能！");
            }
            // 只有当满足文件格式时才进来，重新赋文件名，防止出现名称重复的情况
            newFileName = RandomStringUtils.randomAlphanumeric(8) + "-" + fileName + "." + fileSuffix;
            FileUtils.saveToFile(inDirStr + newFileName, content, "UTF-8");
            inFile = new File(inDir, newFileName);
        } else {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件或输入域两者其一不能为空！");
        }
        //
        if (content == null) {
            uploadFileResult.setResultContent(FileUtils.readFileBytes(inDirStr + newFileName));
        }

        //后端访问地址
        String inputPath = inFile.getAbsolutePath();


        Boolean minify = uploadFileRequest.getMinify();
        if (minify == null) {
            minify = false;
        }
        String result = null;
        //输出文件只有在 Format和转换之间产生
        String outputPath = webAddress + "/uploads/" + loginUser.getId() + "/output/";
        boolean flag = false; //标志是否存入了文件
        //重新获取文件名
        int index = newFileName.lastIndexOf('.');
        fileName = newFileName.substring(0, index);
        if (doType.contains("Format")) {
            flag = true;
            fileName = "f-" + newFileName;
            outputPath = outputPath + fileName; //处理的上传后的文件路径
            if ("jsonFormat".equals(doType)) {

                result = JsonUtils.jsonFormatter(inputPath);
            } else if ("xmlFormat".equals(doType)) {
                result = XmlUtils.xmlFormatter(inputPath);
            } else if ("csvFormat".equals(doType)) {
                CsvUtils.csvFormatter(inputPath, outDirStr + fileName);
                uploadFileResult.setResult(FileUtils.readFileToString(outDirStr + fileName, "UTF-8"));
                uploadFileResult.setOutputPath(outputPath);
            } else if ("yamlFormat".equals(doType)) {
                result = YamlUtils.yamlFormatter(inputPath);
            } else {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "暂不支持此类文件格式化");
            }
        } else if (doType.contains("Valid")) {
            if ("jsonValid".equals(doType)) {
                uploadFileResult.setValid(JsonUtils.jsonValidator(inputPath));
            } else if ("xmlValid".equals(doType)) {
                uploadFileResult.setValid(XmlUtils.xmlValidator(inputPath));
            } else if ("csvValid".equals(doType)) {
                uploadFileResult.setValid(CsvUtils.csvValidator(inputPath, ","));
            } else if ("yamlValid".equals(doType)) {
                uploadFileResult.setValid(YamlUtils.yamlValidator(inputPath));
            } else {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "暂不支持此类文件格式验证");
            }
            if (uploadFileResult.getValid()) {
                uploadFileResult.setResult("格式正确");
            } else {
                uploadFileResult.setResult("格式错误");
            }
        } else if (doType.contains("2")) {
            flag = true;
            String[] split = doType.split("2");
            fileSuffix = split[1];
            fileName = fileName + "." + split[1];
            outputPath = outputPath + fileName; //处理的上传后的文件路径
            if ("json2csv".equals(doType)) {
                result = JsonUtils.json2Csv(inputPath, ",");
            } else if ("json2xml".equals(doType)) {
                result = JsonUtils.json2Xml(inputPath, "root", "\t");
            } else if ("json2yaml".equals(doType)) {
                result = JsonUtils.json2Yaml(inputPath);
            } else if ("csv2json".equals(doType)) {
                CsvUtils.csv2Json(inputPath, outDirStr + fileName);
                uploadFileResult.setResult(FileUtils.readFileBytes(outDirStr + fileName));
                uploadFileResult.setOutputPath(outputPath);
            } else if ("csv2xml".equals(doType)) {
                CsvUtils.csv2Xml(inputPath, outDirStr + fileName);
                uploadFileResult.setResult(FileUtils.readFileBytes(outDirStr + fileName));
                uploadFileResult.setOutputPath(outputPath);
            } else if ("csv2yaml".equals(doType)) {
                CsvUtils.csv2Yaml(inputPath, outDirStr + fileName);
                uploadFileResult.setResult(FileUtils.readFileBytes(outDirStr + fileName));
                uploadFileResult.setOutputPath(outputPath);
            } else if ("xml2json".equals(doType)) {
                result = XmlUtils.xml2Json(inputPath);
            } else if ("xml2csv".equals(doType)) {
                result = XmlUtils.xml2Csv(inputPath);
            } else if ("xml2yaml".equals(doType)) {
                result = XmlUtils.xml2Yaml(inputPath);
            } else if ("yaml2json".equals(doType)) {
                result = YamlUtils.yaml2Json(inputPath);
            } else if ("yaml2xml".equals(doType)) {
                result = YamlUtils.yaml2Xml(inputPath, "root");
            } else if ("yaml2csv".equals(doType)) {
                result = YamlUtils.yaml2Csv(inputPath);
            } else {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "暂不支持此类文件转换");
            }
        } else if (doType.contains("Compress")) {
            minify = false;
            flag = true;
            fileName = "compress-" + newFileName;
            outputPath = outputPath + fileName; //处理的上传后的文件路径
            if ("xmlCompress".equals(doType)) {
                result = XmlUtils.convertFromXml(inputPath);
            } else if ("jsonCompress".equals(doType)) {
                String jsonStr = FileUtils.readFileToString(inputPath, "UTF-8");
                result = JsonUtils.jsonCompress(jsonStr);
            } else {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "暂不支持此类文件压缩");
            }
        } else {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "本程序暂不提供此类服务");
        }


        if (minify && "json".equals(fileSuffix)) {
            if (StringUtils.isNotBlank(result)) {
                result = JsonUtils.jsonCompress(result);
            }
        }

        if (result != null) {
            FileUtils.saveToFile(outDirStr + fileName, result, "UTF-8");
            //最多只读取文件的前50行
            uploadFileResult.setResult(FileUtils.readFileBytes(outDirStr + fileName));
            uploadFileResult.setOutputPath(outputPath);
        }

        if (minify && "xml".equals(fileSuffix)) {
            if (StringUtils.isNotBlank(result)) {
                result = XmlUtils.convertFromXml(outDirStr + fileName);
                FileUtils.saveToFile(outDirStr + fileName, result, "UTF-8");
                uploadFileResult.setResult(FileUtils.readFileBytes(outDirStr + fileName));
            }
        }


        //获取上传文件后的路径   前端访问地址
        inputPath = webAddress + "/uploads/" + loginUser.getId() + "/input/" + newFileName;
        uploadFileResult.setInputPath(inputPath);

        FileRecord fileRecord = new FileRecord();
        fileRecord.setUserId(userId);
        fileRecord.setDoType(doType);
        fileRecord.setInputPath(inputPath);
        if (flag) { //存入了文件则记录文件地址
            fileRecord.setOutputPath(outputPath);
        }
//        fileRecord.setContent("");
        //添加文件记录
        boolean is = this.save(fileRecord);
        if (!is) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }

        //更新用户使用次数
        loginUser.setUpdateTime(new Date());
        loginUser.setCurrentCount(loginUser.getCurrentCount() + 1);
        is = userService.updateById(loginUser);
        if (!is) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }
        return uploadFileResult;
    }


    /**
     * 验证文件的格式
     *
     * @param file 文件
     * @return 是否符合指定的文件类型
     */
    private boolean fileTypeRight(MultipartFile file) {
        // 首先校验图片格式
        List<String> fileType = Arrays.asList("csv", "json", "yaml", "xml");
        // 获取文件名，带后缀
        String originalFilename = file.getOriginalFilename();
        // 获取文件的后缀格式
        assert originalFilename != null;
        String fileSuffix = originalFilename.substring(originalFilename.lastIndexOf(".") + 1).toLowerCase();  //不带 .
        return fileType.contains(fileSuffix);
    }
}




